package org.rbreznak.tronco.client;

import java.awt.Cursor;
import java.awt.Frame;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.EventObject;
import javax.swing.JFrame;
import org.rbreznak.tronco.sql.DatabaseHandler;
import org.rbreznak.tronco.sql.MySQLConnection;

public class Tronco extends JFrame implements ScreenListener, KeyListener, MouseListener{

	private static final long serialVersionUID = -5436196588439522369L;
	
	private Screen current;		// the currently displayed screen
	private final Screen root;	// the first screen
	
	// the database connection. will be passed along to the current screen 
	private final DatabaseHandler DBH = new DatabaseHandler(new MySQLConnection("mysql.wpi.edu", "tronco3", "n7q25j", "tronco3"));
	
	/**
	 * Tronco
	 */
	public Tronco() {
		// setup the windowed version so it doesn't look terrible
		this.setSize(600, 800);
		this.setTitle("Tronco");
		
		// when the window closes, kill the program
		this.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
		
		// remove the windows headers (minimize, window and close buttons)
		this.setUndecorated(true);
		// make the frame fullscreen
		this.setExtendedState(Frame.MAXIMIZED_BOTH);
		
		// set the mouse cursor (blank or a finger)
		Image cursorImage = Toolkit.getDefaultToolkit().getImage("img/default.png");
		Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, new Point(10, 2), "" );
		this.setCursor(blankCursor);
		
		// make the frame visable
		this.setVisible(true);
		
		// declare all of the screens ahead of time
		// TODO: Find a better way to do this. 
		Screen1 sn1 = new Screen1();
		Screen2 sn2 = new Screen2();
		Screen3 sn3 = new Screen3();
		Screen4 sn4 = new Screen4();
		Screen5 sn5 = new Screen5();
		ScreenLast sn6 = new ScreenLast();

		// link the screens together
		// TODO: Find a better way to do this.
		sn1.addNode(sn2);
		sn2.addNode(sn3);
		sn3.addNode(sn4);
		sn4.addNode(sn5);
		sn5.addNode(sn6);
		
		// root is the first screen, load it up
		root = sn1;		
		setScreen(root);
		
		// set the frame (this) to listen for all key actions
		this.addKeyListener(this);
	}

	/**
	 * Replaces the current screen with the one given
	 * 
	 * @param screen
	 */
	public void setScreen(Screen screen) {		
		// make sure that the given screen is valid
		if(screen != null) {
			
			// if there is a current screen remove it's listeners
			if(current != null) {
				current.removeKeyListener(this);
				current.removeMouseListener(this);
			}
			
			// set the current screen to the given one
			current = screen;
			
			// give database control to the new screen
			current.dbh = DBH;
			
			// setup the listeners from the current screen to the frame
			current.addScreenListener(this);
			current.addMouseListener(this);
			
			// put the current screen into the frame, repaint and refocus
			this.setContentPane(current);
			current.revalidate();
			this.requestFocus();
			
			// run any initializations that the screen may have
			current.init();
		}
	}
	
	/**
	 * 
	 * return the currently displayed screen
	 * 
	 * @return
	 * 
	 */
	public Screen getScreen() {
		return current;
	}
	
	/**
	 * 
	 * called when a screenUpdate is fired.
	 * prepares the current screen to be swapped for the next one
	 * 
	 * @param event the screen that fired the update
	 * 
	 */
	public void screenUpdate(EventObject event) {
		// make sure that only the current screen can fire an update
		if(event.getSource() != current) {
			return;
		}
		
		if(current != null) {
			// the currently displayed screen is used to get the next screen
			if(current.getNext() == null) {
				// no next screen is given, restart the flow
				reset(root);
				setScreen(root);
			} else {
				// the next screen is given, so display it
				setScreen(current.getNext());
			}
		} else {
			// there is no currently displayed screen, reset the enture flow
			reset(root);
		}
	}
	
	/**
	 * 
	 * reset any internals necessary for the screen for a seamless restart
	 * 
	 * @param node the screen that will be reset
	 */
	private void reset(Screen node) {
		// make sure that the screen is valid
		if(node != null) {
			// reset all of it's children
			for(int i=0;i<node.nodes.size();i++){
				reset(node.nodes.get(i));
			}
			
			// reset itself
			node.reset();
		}
	}

	/**
	 * 
	 * listener for the action
	 * passes to the current screen only
	 * 
	 * @param arg0 the event
	 * 
	 */
	public void keyPressed(KeyEvent arg0) {
		current.keyPressed(arg0);
	}
	
	/**
	 * 
	 * listener for the action
	 * passes to the current screen only
	 * 
	 * @param arg0 the event
	 * 
	 */
	public void keyReleased(KeyEvent arg0) {
		current.keyReleased(arg0);
	}

	/**
	 * 
	 * listener for the action
	 * passes to the current screen only
	 * 
	 * @param arg0 the event
	 * 
	 */
	public void keyTyped(KeyEvent arg0) {
		current.keyTyped(arg0);
	}

	/**
	 * 
	 * listener for the action
	 * passes to the current screen only
	 * 
	 * @param arg0 the event
	 * 
	 */
	public void mouseClicked(MouseEvent arg0) {
		current.mouseClicked(arg0);
	}

	/**
	 * 
	 * listener for the action
	 * passes to the current screen only
	 * 
	 * @param arg0 the event
	 * 
	 */
	public void mouseEntered(MouseEvent arg0) {
		current.mouseEntered(arg0);
	}

	/**
	 * 
	 * listener for the action
	 * passes to the current screen only
	 * 
	 * @param arg0 the event
	 * 
	 */
	public void mouseExited(MouseEvent arg0) {
		current.mouseExited(arg0);
	}

	/**
	 * 
	 * listener for the action
	 * passes to the current screen only
	 * 
	 * @param arg0 the event
	 * 
	 */
	public void mousePressed(MouseEvent arg0) {
		current.mousePressed(arg0);
	}

	/**
	 * 
	 * listener for the action
	 * passes to the current screen only
	 * 
	 * @param arg0 the event
	 * 
	 */
	public void mouseReleased(MouseEvent arg0) {
		current.mouseReleased(arg0);
	}
	
}
